﻿/*
    TwoPageView image viewer
    Copyright (C) 2006 Japan Manoretimedia Lab.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Windows.Forms;

namespace TwoPageViewThumbnailCreator
{
    class Program
    {
        enum RotateType
        {
            Zero,
            H90,
            H270,
            V90,
            V270
        }
        RotateType rotate;
        System.Drawing.Size thumbSize;
        System.Drawing.Drawing2D.InterpolationMode interpolationMode;

        private System.Drawing.Bitmap BitmapLoadFromFile(string path)
        {
            System.Drawing.Bitmap bm = null;
            try {
                using (System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) {
                    // This really looks weird but it definitely is needed to avoid the OutOfMemoryException on Windows2000!
                    /*System.Drawing.Bitmap bm1 = new System.Drawing.Bitmap(fs);
                    bm = new System.Drawing.Bitmap(bm1);
                    bm1.Dispose();
                    bm1 = null;
                    */
                    bm = new System.Drawing.Bitmap(fs);
                }
                // Do not add animated GIFs (picturebox animegif support is difficult to use)
                if (1 != bm.GetFrameCount(new System.Drawing.Imaging.FrameDimension(bm.FrameDimensionsList[0]))) {
                    bm.Dispose();
                    return null;
                }
                switch (rotate) {
                case RotateType.H90:
                    if (bm.Width < bm.Height) {
                        bm.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
                    }
                    break;
                case RotateType.H270:
                    if (bm.Width < bm.Height) {
                        bm.RotateFlip(System.Drawing.RotateFlipType.Rotate270FlipNone);
                    }
                    break;
                case RotateType.V90:
                    if (bm.Height < bm.Width) {
                        bm.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
                    }
                    break;
                case RotateType.V270:
                    if (bm.Height < bm.Width) {
                        bm.RotateFlip(System.Drawing.RotateFlipType.Rotate270FlipNone);
                    }
                    break;
                default:
                    break;
                }
            } catch (System.ArgumentException) {
            } catch (System.IO.IOException) {
            } catch (System.UnauthorizedAccessException) {
            } catch (System.Runtime.InteropServices.ExternalException) {
            } catch (System.Exception e) {
#if DEBUG
                MessageBox.Show(string.Format("D: BitmapLoadFromFile({0}) {1}", path, e));
#endif // Debug
            }
            return bm;
        }

        static System.Drawing.Size ThumbSizeOrientationCalc(System.Drawing.Size imageSize, System.Drawing.Size thumbnailSize)
        {
            System.Drawing.Size rv = thumbnailSize;
            if ((0 == (imageSize.Width / imageSize.Height)) !=
                (0 == (thumbnailSize.Width / thumbnailSize.Height)))
            {
                rv.Width = thumbnailSize.Height;
                rv.Height = thumbnailSize.Width;
            }
            return rv;
        }

        private System.Drawing.Bitmap BitmapResample_KeepAspectRatio(System.Drawing.Bitmap bm, System.Drawing.Size newSize)
        {
            newSize = ThumbSizeOrientationCalc(bm.Size, newSize);
            if ((float)newSize.Width / newSize.Height < (float)bm.Width / bm.Height)
            {
                newSize.Height = bm.Height * newSize.Width / bm.Width;
                if (0 == newSize.Height)
                {
                    newSize.Height = 1;
                }
            }
            else
            {
                newSize.Width = bm.Width * newSize.Height / bm.Height;
                if (0 == newSize.Width)
                {
                    newSize.Width = 1;
                }
            }
            System.Drawing.Bitmap newBM = new System.Drawing.Bitmap(newSize.Width, newSize.Height);
            try
            {
                using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(newBM))
                {
                    g.InterpolationMode = interpolationMode;
                    g.DrawImage(bm, 0, 0, newSize.Width, newSize.Height);
                }
            }
            catch (System.Runtime.InteropServices.ExternalException)
            {
                newBM.Dispose();
                newBM = null;
            }
            return newBM;
        }

        private bool BitmapLoadFromFileAndResample(string path, out System.Drawing.Bitmap bm, out System.Drawing.Size originalWH)
        {
            bm = BitmapLoadFromFile(path);
            if (null == bm)
            {
                originalWH = new System.Drawing.Size(0, 0);
                return false;
            }
            originalWH = bm.Size;
            System.Drawing.Bitmap resampledBM = BitmapResample_KeepAspectRatio(bm, thumbSize);
            bm.Dispose();
            bm = resampledBM;
            return true;
        }

        private string Base64ToStr(string s)
        {
            byte[] b = Convert.FromBase64String(s);
            char[] cArray = new char[b.Length/2];

            for (int i=0; i<cArray.Length; ++i) {
                cArray[i] = (char)((int)b[i * 2] + ((int)b[i * 2 + 1] << 8));
            }
            string ret = new string(cArray);
            return ret;
        }

        private void Run(BinaryWriter bw)
        {
            do
            {
                // input:
                //   filePath
                //   RotateFlipType
                //   Size.w
                //   Size.h
                //   InterpolationMode

                // output when succeeded:
                //     int bitmapSize
                //     OriginalSizeWH
                //     Bitmap
                // output when failed:
                //     int 0
                string s = System.Console.ReadLine();
                if (null == s)
                {
                    System.Console.WriteLine("D: null == filePath!");
                    break;
                }
                string filePath = Base64ToStr(s);
                System.Console.WriteLine("D: filePath={0}", filePath);

                string sRotate = System.Console.ReadLine();
                if (null == sRotate)
                {
                    System.Console.WriteLine("D: null == sRotate");
                    break;
                }
                System.Console.WriteLine("D: sRotate={0}", sRotate);
                switch (sRotate)
                {
                    case "90":
                        rotate = RotateType.H90; break;
                    case "270":
                        rotate = RotateType.H270; break;
                    case "v90":
                        rotate = RotateType.V90; break;
                    case "v270":
                        rotate = RotateType.V270; break;
                    default:
                        rotate = RotateType.Zero; break;
                }

                string sW = System.Console.ReadLine();
                if (null == sW)
                {
                    System.Console.WriteLine("D: null == sW");
                    break;
                }
                System.Console.WriteLine("D: sW={0}", sW);
                thumbSize.Width = Int32.Parse(sW);

                string sH = System.Console.ReadLine();
                if (null == sH)
                {
                    System.Console.WriteLine("D: null == sH");
                    break;
                }
                System.Console.WriteLine("D: sH={0}", sH);
                thumbSize.Height = Int32.Parse(sH);

                string sInterpolationMode = System.Console.ReadLine();
                if (null == sInterpolationMode)
                {
                    System.Console.WriteLine("D: null == sInterpolationMode");
                    break;
                }
                switch (sInterpolationMode) {
                    case "Bicubic":             interpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bicubic; break;
                    case "Bilinear":            interpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; break;
                    case "High":                interpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; break;
                    case "HighQualityBicubic":  interpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; break;
                    case "HighQualityBilinear": interpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; break;
                    case "Low":                 interpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low; break;
                    case "NearestNeighbor":     interpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; break;
                    default:                    interpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; break;
                }
 
                System.Drawing.Bitmap bm;
                System.Drawing.Size originalWH;
                if (BitmapLoadFromFileAndResample(filePath, out bm, out originalWH) && null != bm) {
                    MemoryStream ms = new MemoryStream();
                    int size = 0;

                    bm.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                    byte[] bytes = ms.ToArray();
                    size = bytes.Length;
                    bw.Write(size);
                    bw.Write(originalWH.Width);
                    bw.Write(originalWH.Height);
                    bw.Write(bytes, 0, size);
                    bm.Dispose();
                    bm = null;
                } else {
                    int size = 0;
                    bw.Write(size);
                }
            } while (true);
        }

        static void Main(string[] args)
        {
            System.Console.WriteLine("D: TwoPageViewThumbnailCreator started. args.Length={0}", args.Length);
            if (args.Length > 0)
            {
                using (PipeStream pipeClient = new AnonymousPipeClientStream(PipeDirection.Out, args[0]))
                {
                    using (BinaryWriter bw = new BinaryWriter(pipeClient))
                    {
                        try {
                            Program p = new Program();
                            p.Run(bw);
                        } catch (System.IO.IOException) { 
                        } catch (System.Exception ex) {
#if DEBUG
                            MessageBox.Show(ex.ToString());
#endif // Debug
                        }
                    }
                }
            }
            System.Console.WriteLine("D: TwoPageViewThumbnailCreator exit");
        }
    }
}
